﻿using Adaptive.Agrona.Concurrent;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

using Unity.Connections;
using Unity.Commons.Client.Generator;
using Unity.Commons.Client.Statistics;
using Unity.Credentials;

namespace Unity.Commons.Client.Threading
{
    
    // TODO
    public class UnityResourceThread : IUnityResourceThread
    {
        private readonly IUnityClient _client; 
        private readonly IUnityProducer _resourceProducer;
        
        private readonly List<IResourceTask> _tasks;
        private readonly ConcurrentQueue<UnityGenerator> _generators;

        private readonly string _csiId;

        private bool _isStopped = false;

        private readonly AtomicReference<TaskCompletionSource<bool>> _asyncTaskResult 
            = new AtomicReference<TaskCompletionSource<bool>>();
        private readonly AtomicReference<Func<bool>> _asyncTaskCondition 
            = new AtomicReference<Func<bool>>();

        private const int MakeGeneratorLimit = 10;
        
        private static readonly UnityStatisticsSerializer StatisticsSerializer 
            = new UnityStatisticsSerializer();
        private long _lastStatusSyncTime;
        private const long StatusSyncPeriod = 10 * 60_000_000_000;


        public UnityResourceThread(IUnityClient client, IUnityProducer producer, string csiId)
        {
            _client = client;
            _resourceProducer = producer;
            _csiId = csiId;
            
            _generators = new ConcurrentQueue<UnityGenerator>();

            

          
            Func<bool> checkGeneratorsFunc = () => {
                UnityGenerator newGenerator = new UnityGenerator(_csiId);
                _generators.Enqueue(newGenerator);
                return true;
            };
            Func<bool> checkGeneratorsCondition = () => {
                return _generators.Count < MakeGeneratorLimit;
            };

            Func<bool> checkConnectionFunc = () => { return true; };
            Func<bool> checkConnectionCondition = () => { return false; };

            Func<bool> syncStatusFunc = () => { return true; };
            Func<bool> syncStatusCondition = () => { return false; };

  
            Func<bool> publishStatisticsFunc = () => {
                _lastStatusSyncTime = UnityTimestamp.ClockTimestamp();
                Console.WriteLine($"Send status. Current time: {_lastStatusSyncTime}");
                _resourceProducer.Produce(StatisticsSerializer.SerializeStatus(
                _client.GetStatistics(), _csiId), 0, UnityStatisticsSerializer.StatusLength);
                return true; 
            };
            Func<bool> publishStatisticsCondition = () => {
                long currentUnixTime = UnityTimestamp.ClockTimestamp();
                return currentUnixTime > _lastStatusSyncTime + StatusSyncPeriod;
            };

            
            _tasks = new List<IResourceTask>()
            {
                new ResourceTask(
                    checkGeneratorsFunc,
                    checkGeneratorsCondition
                ),
                new ResourceTask(
                    checkConnectionFunc,
                    checkConnectionCondition
                ),
                new ResourceTask(
                    syncStatusFunc,
                    syncStatusCondition
                ),
                new ResourceTask(
                    publishStatisticsFunc,
                    publishStatisticsCondition
                )

            };
        }


        public void Close()
        {
            _isStopped = true;
            // close producer
        }

        public bool ConnectProducer(IUnityCredentials credentials)
        {
            return false;
        }

        public int DoWork()
        {
            if(_isStopped)
            {
                return 0;
            }
            int runResult = 0;

            _tasks.ForEach(task =>
            {
                if(task.IsNeedRun())
                {
                    task.Run();
                    runResult++;
                }
            });

            return runResult;
        }

        //public ResourceTask<bool>

        private UnityGenerator CreateGenerator()
        {
            return new UnityGenerator(_csiId);
        }

        public UnityGenerator GetNewGenerator()
        {
            UnityGenerator newGenerator;
            if (!_generators.TryDequeue(out newGenerator))
            {
                newGenerator = CreateGenerator();
            } 
            return newGenerator;
        }

        public IUnityProducer GetProducer()
        {
            return _resourceProducer;
        }

        public Task<bool> RunAsync(Func<bool> command)
        {
            _asyncTaskCondition.GetAndSet(command);
            TaskCompletionSource<bool> result = new TaskCompletionSource<bool>();
            _asyncTaskResult.GetAndSet(result);
            return result.Task;
        }

        void IAgent.OnStart()
        {
            // Method intentionally left empty.
        }

        void IAgent.OnClose()
        {
            // Method intentionally left empty.
        }

        public string RoleName()
        {
            return "resource-thread";
        }

    }
}
